﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;


namespace OF.Utility
{
    /// <summary>
    /// 系统数据类型 “时间”扩展方法类
    /// </summary>
    public static class DateTimeHelper
    {
        #region Extension Methods

        #region DaysInMonth
        /// <summary>
        /// 将日期时间类型转换为可供数据库授受的类型
        /// </summary>
        /// <param name="Date"></param>
        /// <returns></returns>
        public static object ToNullDbDateTime(this DateTime Date)
        {
            object oDate = Date;
            if (Date.IsNullDateTime())
            {
                oDate = new DateTime(1900, 1, 1);
            }
            return oDate;
        }

        /// <summary>
        /// 判断日期时间类型是否为空
        /// </summary>
        /// <param name="Date"></param>
        /// <returns></returns>
        public static bool IsNullDateTime(this DateTime Date)
        {
            bool bReturn = Date.Year == 1900 || Date == DateTime.MinValue;
            return bReturn;
        }

        /// <summary>
        /// 按指定格式显示时间
        /// </summary>
        /// <param name="Date"></param>
        /// <param name="format"></param>
        /// <returns></returns>
        public static string ToDisplay(this DateTime Date, string format)
        {
            string szReturn = string.Empty;
            if (Convert.ToDateTime("1900-1-1") == Date || Date == DateTime.MinValue)
            {

            }
            else
            {
                szReturn = Date.ToString(format);
            }
            return szReturn;

        }
        /// <summary>
        /// 获取日期时间所在月份总共有多少天
        /// </summary>
        /// <param name="Date">日期时间</param>
        /// <returns><c>int</c></returns>
        public static int DaysInMonth(this DateTime Date)
        {
            return Date.LastDayOfMonth().Day;
        }

        #endregion

        #region DaysLeftInMonth

        /// <summary>
        /// 获取日期时间所在月份还剩余多少天
        /// </summary>
        /// <param name="Date">The date to check against</param>
        /// <returns>The number of days left in a month</returns>
        public static int DaysLeftInMonth(this DateTime Date)
        {
            return Thread.CurrentThread.CurrentCulture.Calendar.GetDaysInMonth(Date.Year, Date.Month) - Date.Day;
        }

        #endregion

        #region DaysInYear

        /// <summary>获取一个日期所在年份共有多少天</summary>
        /// <param name="Date">要查看的日期</param>
        /// <returns>日期时间所在年份拥有的天数</returns>
        public static int GetDaysOfYear(this DateTime Date)
        {
            return Thread.CurrentThread.CurrentCulture.Calendar.GetDaysInYear(Date.Year);
        }

        #endregion

        #region DaysLeftInYear

        /// <summary> 
        /// 获取日期时间所在年份还剩余多少天
        /// </summary>
        /// <param name="Date">传入时间</param>
        /// <returns>时间所在年份剩余的天数</returns>
        public static int DaysLeftInYear(this DateTime Date)
        {
            return Thread.CurrentThread.CurrentCulture.Calendar.GetDaysInYear(Date.Year) - Date.DayOfYear;
        }

        #endregion

        #region DaysLeftInWeek

        /// <summary>
        /// 获取日期时间所在周份还剩余的天数
        /// </summary>
        /// <param name="Date">传入时间</param>
        /// <returns>时间所在周份还剩余的天数</returns>
        public static int DaysLeftInWeek(this DateTime Date)
        {
            return 7 - ((int)Date.DayOfWeek + 1);
        }

        #endregion

        #region FirstDayOfMonth

        /// <summary>
        /// 根据日期时间获取获取其所在月份的第一天
        /// </summary>
        /// <param name="Date">传入时间</param>
        /// <returns>第1天的时间<c>DateTime</c></returns>
        public static DateTime FirstDayOfMonth(this DateTime Date)
        {
            return new DateTime(Date.Year, Date.Month, 1);
        }

        #endregion

        #region FirstDayOfWeek

        /// <summary>
        /// 获取一周中的第一天的具体日期时间
        /// </summary>
        /// <param name="Date">传入参数</param>
        /// <param name="CultureInfo">本地化信息</param>
        /// <returns>第一天的具体日期时间</returns>
        public static DateTime FirstDayOfWeek(this DateTime Date, CultureInfo CultureInfo = null)
        {
            return Date.AddDays(CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek - Date.DayOfWeek).Date;
        }

        #endregion

        #region LastDayOfMonth

        /// <summary>
        /// 获取一个时间所在月份的最后一天
        /// </summary>
        /// <param name="Date">要获取其所在月份最后一天的时间</param>
        /// <returns>最后一天的日期时间</returns>
        public static DateTime LastDayOfMonth(this DateTime Date)
        {
            return Date.AddMonths(1).FirstDayOfMonth().AddDays(-1).Date;
        }

        #endregion

        #region LastDayOfWeek

        /// <summary>
        /// 获取一个时间所在周份的最后一天
        /// </summary>
        /// <param name="Date">要获取其所在周份最后一天的时间</param>
        /// <param name="CultureInfo">本地化信息</param>
        /// <returns>最后一天的日期时间</returns>
        public static DateTime LastDayOfWeek(this DateTime Date, CultureInfo CultureInfo = null)
        {
            return Date.FirstDayOfWeek(CultureInfo.CurrentCulture).AddDays(6);
        }

        #endregion

        #region FromUnixTime

        /// <summary>
        /// 转化Unix时间为.net中的时间类型
        /// </summary>
        /// <param name="Date">Unix时间</param>
        /// <returns><c>DataTime</c>格式的时间表示类型</returns>
        public static DateTime FromUnixTime(this int Date)
        {
            return new DateTime((Date * TimeSpan.TicksPerSecond) + new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks, DateTimeKind.Utc);
        }

        /// <summary>
        /// 转化Unix时间为.net中的时间类型
        /// </summary>
        /// <param name="Date">要转化Unix时间</param>
        /// <returns><c>DataTime</c>格式的时间表示类型</returns>
        public static DateTime FromUnixTime(this long Date)
        {
            return new DateTime((Date * TimeSpan.TicksPerSecond) + new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks, DateTimeKind.Utc);
        }

        #endregion

        #region ToUnix

        /// <summary>
        /// 将.net中的日期时间转化为Unix时间格式
        /// </summary>
        /// <param name="Date">要转化的.net日期时间</param>
        /// <returns>为Unix时间<c>int</c></returns>
        public static int ToUnix(this DateTime Date)
        {
            return (int)((Date.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).Ticks / TimeSpan.TicksPerSecond);
        }

        #endregion

        #region IsInFuture

        /// <summary>
        /// 检查一个时间是否为将来时间
        /// </summary>
        /// <param name="Date">要检查的时间</param>
        /// <returns>True if it is, false otherwise</returns>
        public static bool IsInFuture(this DateTime Date)
        {
            return DateTime.Now < Date;
        }

        #endregion

        #region IsInPast

        /// <summary>
        /// 检查一个时间是否为过去时间
        /// </summary>
        /// <param name="Date">要检查的时间</param>
        /// <returns>True if it is, false otherwise</returns>
        public static bool IsInPast(this DateTime Date)
        {
            return DateTime.Now > Date;
        }

        #endregion

        #region IsWeekDay

        /// <summary>
        /// 检查一个时间是否为工作时(即周一至周五)
        /// </summary>
        /// <param name="Date">要检查的时间</param>
        /// <returns>是则返回true,否则返回false</returns>
        public static bool IsWeekDay(this DateTime Date)
        {
            return (int)Date.DayOfWeek < 6 && (int)Date.DayOfWeek > 0;
        }

        #endregion

        #region IsWeekEnd

        /// <summary>
        /// 检查一个时间是否为双休日
        /// </summary>
        /// <param name="Date">要检查的时间</param>
        /// <returns>是则返回true,否则返回false</returns>
        public static bool IsWeekEnd(this DateTime Date)
        {
            return !IsWeekDay(Date);
        }
        #endregion

        #region 今天是星期几
        /// <summary>获取日期时间是星期几</summary>
        /// <param name="idt">日期</param>
        /// <returns>星期名称</returns>
        /// <remarks>
        /// 返回如：周一，周二，周三..周日的表示形式
        /// </remarks>
        public static string GetWeekNameOfDay(this DateTime idt)
        {
            string dt, week = "";

            dt = idt.DayOfWeek.ToString();
            switch (dt)
            {
                case "Mondy":
                    week = "周一";
                    break;
                case "Tuesday":
                    week = "周二";
                    break;
                case "Wednesday":
                    week = "周三";
                    break;
                case "Thursday":
                    week = "周四";
                    break;
                case "Friday":
                    week = "周五";
                    break;
                case "Saturday":
                    week = "周六";
                    break;
                case "Sunday":
                    week = "周日";
                    break;

            }
            return week;
        }

        /// <summary>返回日期时间的星期编号</summary>
        /// <param name="idt">日期</param>
        /// <returns>星期数字编号</returns>
        /// <remarks>
        /// 返回 1，2，3，4，5，6，7的表示形式
        /// </remarks>
        public static int GetWeekNumberOfDay(this DateTime idt)
        {
            string dt;
            int week = 1;

            dt = idt.DayOfWeek.ToString();
            switch (dt)
            {
                case "Mondy":
                    week = 1;
                    break;
                case "Tuesday":
                    week = 2;
                    break;
                case "Wednesday":
                    week = 3;
                    break;
                case "Thursday":
                    week = 4;
                    break;
                case "Friday":
                    week = 5;
                    break;
                case "Saturday":
                    week = 6;
                    break;
                case "Sunday":
                    week = 7;
                    break;

            }

            return week;
        }
        #endregion
        #endregion

        /// <summary>
        /// 将时间格式化成 年月日 的形式,如果时间为null，返回当前系统时间
        /// </summary>
        /// <param name="dt">要格式化的日期</param>
        /// <param name="Separator">年月日分隔符,如：“-”，“/"，“：”等等..</param>
        /// <returns>格式化后的日期字符串</returns>
        public static string GetFormatDate(this DateTime dt, char Separator = '-')
        {
            if (dt != null && !dt.Equals(DBNull.Value))
            {
                string tem = string.Format("yyyy{0}MM{1}dd", Separator, Separator);
                return dt.ToString(tem);
            }
            else
            {
                return GetFormatDate(DateTime.Now, Separator);
            }
        }

        /// <summary>
        /// 将时间格式化成 时分秒 的形式,如果时间为null，返回当前系统时间
        /// </summary>
        /// <param name="dt">要格式化的日期时间</param>
        /// <param name="Separator">年月日分隔符,如：“：”等等..</param>
        /// <returns>格式化的时间格式</returns>
        /// <example>
        /// 
        ///  </example>
        public static string GetFormatTime(this DateTime dt, char Separator)
        {
            if (dt != null && !dt.Equals(DBNull.Value))
            {
                string tem = string.Format("hh{0}mm{1}ss", Separator, Separator);
                return dt.ToString(tem);
            }
            else
            {
                return GetFormatDate(DateTime.Now, Separator);
            }
        }
        /// <summary>
        /// 把秒转换成分钟
        /// </summary>
        /// <returns>转换后的分钟数</returns>
        public static int SecondToMinute(int Second)
        {
            decimal mm = (decimal)((decimal)Second / (decimal)60);
            return Convert.ToInt32(Math.Ceiling(mm));
        }

        #region 返回某年某月最后一天
        /// <summary>
        /// 返回某年某月最后一天
        /// </summary>
        /// <param name="year">年份</param>
        /// <param name="month">月份</param>
        /// <returns>日</returns>
        public static int GetMonthLastDate(int year, int month)
        {
            DateTime lastDay = new DateTime(year, month, new GregorianCalendar().GetDaysInMonth(year, month));
            int Day = lastDay.Day;
            return Day;
        }
        #endregion

        #region 返回时间差
        /// <summary>
        /// 返回时间差中的字符串表示形式
        /// <remarks>
        /// 原理：<para>如果时间差大于等于1天则返回形如:“5月19日”即 M月D日的形式</para>
        /// <para>如果时间差大于1小时，则返回形如：“12小时前“的形式</para>
        /// <para>否则返回“｛分钟数｝分钟前”的形式，如：5分钟前</para>
        /// </remarks>
        /// </summary>
        /// <param name="DateTime1"></param>
        /// <param name="DateTime2"></param>
        /// <returns></returns>
        public static string DateDiff(DateTime DateTime1, DateTime DateTime2)
        {
            string dateDiff = null;
            try
            {
                TimeSpan ts = DateTime2 - DateTime1;
                if (ts.Days >= 1)
                {
                    dateDiff = DateTime1.Month + "月" + DateTime1.Day + "日";
                }
                else
                {
                    if (ts.Hours > 1)
                    {
                        dateDiff = ts.Hours + "小时前";
                    }
                    else
                    {
                        dateDiff = ts.Minutes + "分钟前";
                    }
                }
            }
            catch (Exception)
            {

            }
            return dateDiff;
        }
        #endregion

        #region 获得两个日期的间隔
        /// <summary>
        /// 获得两个日期的间隔
        /// </summary>
        /// <param name="DateTime1">日期一。</param>
        /// <param name="DateTime2">日期二。</param>
        /// <returns>日期间隔TimeSpan。</returns>
        public static TimeSpan DateDiff2(DateTime DateTime1, DateTime DateTime2)
        {
            TimeSpan ts1 = new TimeSpan(DateTime1.Ticks);
            TimeSpan ts2 = new TimeSpan(DateTime2.Ticks);
            TimeSpan ts = ts1.Subtract(ts2).Duration();
            return ts;
        }
        #endregion

        #region 格式化日期时间
        /// <summary>
        /// 格式化日期时间
        /// </summary>
        /// <param name="dateTime1">日期时间</param>
        /// <param name="dateMode">显示模式</param>
        /// <returns>0-9种模式的日期</returns>
        /// <remarks>
        /// 0: "yyyy-MM-dd"
        /// 1:"yyyy-MM-dd HH:mm:ss"
        /// 2:"yyyy/MM/dd"
        /// 3:"yyyy年MM月dd日"
        /// 4:"MM-dd"
        /// 5:"MM/dd"
        /// 6:"MM月dd日"
        /// 7:"yyyy-MM"
        /// 8:"yyyy/MM"
        /// 9:"yyyy年MM月"
        /// </remarks>
        public static string FormatDate(this DateTime dateTime1, string dateMode)
        {
            switch (dateMode)
            {
                case "0":
                    return dateTime1.ToString("yyyy-MM-dd");
                case "1":
                    return dateTime1.ToString("yyyy-MM-dd HH:mm:ss");
                case "2":
                    return dateTime1.ToString("yyyy/MM/dd");
                case "3":
                    return dateTime1.ToString("yyyy年MM月dd日");
                case "4":
                    return dateTime1.ToString("MM-dd");
                case "5":
                    return dateTime1.ToString("MM/dd");
                case "6":
                    return dateTime1.ToString("MM月dd日");
                case "7":
                    return dateTime1.ToString("yyyy-MM");
                case "8":
                    return dateTime1.ToString("yyyy/MM");
                case "9":
                    return dateTime1.ToString("yyyy年MM月");
                default:
                    return dateTime1.ToString();
            }
        }
        #endregion

        #region 得到随机日期
        /// <summary>
        /// 得到随机日期
        /// </summary>
        /// <param name="time1">起始日期</param>
        /// <param name="time2">结束日期</param>
        /// <returns>间隔日期之间的 随机日期</returns>
        public static DateTime GetRandomTime(DateTime time1, DateTime time2)
        {
            Random random = new Random();
            DateTime minTime = new DateTime();
            DateTime maxTime = new DateTime();

            TimeSpan ts = new TimeSpan(time1.Ticks - time2.Ticks);

            // 获取两个时间相隔的秒数
            double dTotalSecontds = ts.TotalSeconds;
            int iTotalSecontds = 0;

            if (dTotalSecontds > Int32.MaxValue)
            {
                iTotalSecontds = Int32.MaxValue;
            }
            else if (dTotalSecontds < Int32.MinValue)
            {
                iTotalSecontds = Int32.MinValue;
            }
            else
            {
                iTotalSecontds = (int)dTotalSecontds;
            }


            if (iTotalSecontds > 0)
            {
                minTime = time2;
                maxTime = time1;
            }
            else if (iTotalSecontds < 0)
            {
                minTime = time1;
                maxTime = time2;
            }
            else
            {
                return time1;
            }

            int maxValue = iTotalSecontds;

            if (iTotalSecontds <= Int32.MinValue)
                maxValue = Int32.MinValue + 1;

            int i = random.Next(Math.Abs(maxValue));

            return minTime.AddSeconds(i);

        }
        #endregion

        #region 私有成员

        /// <summary>判断当前年份是否是闰年，私有函数</summary>
        /// <param name="iYear">年份</param>
        /// <returns>是闰年：True ，不是闰年：False</returns>
        [Obsolete("过时的方法，因为系统中有自带的API函数")]
        private static bool IsRuYear(int iYear)
        {
            //形式参数为年份
            //例如：2003
            int n;
            n = iYear;

            if ((n % 400 == 0) || (n % 4 == 0 && n % 100 != 0))
            {
                return true;
            }
            return false;
        }
        #endregion

        #region 根据日期，获得星期几
        /// <summary>根据日期，获得星期几</summary>
        /// <param name="y">年</param> 
        /// <param name="m">月</param> 
        /// <param name="d">日</param> 
        /// <returns>星期几，1代表星期一；7代表星期日</returns>
        public static int getWeekDay(int y, int m, int d)
        {
            if (m == 1) m = 13;
            if (m == 2) m = 14;
            int week = (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400) % 7 + 1;
            return week;
        }
        #endregion

        /// <summary>
        /// 默认最小时间
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static DateTime MinDateTime()
        {
            return Convert.ToDateTime("1900-1-1");
        }
    }
}
